#ifndef SUNFLOWER_H_P3WE672Q
#define SUNFLOWER_H_P3WE672Q

/*
 *  sunflower.h
 *  
 *
 *  Created by Jason Hsu, Jian Wei Gan, and Weiping Zhang on 11/17/09.
 *  Copyright 2009. All rights reserved.
 *
 *
 *
 */

#include "util/vec3f.h"
#include "util/math_util.h"
#include "util/color4f.h"
#include "weather/weatherparser.h"
#include "utility.h"

#include "math.h"

#define MAGIC_NUMBER 0.55228475

class Sunflower
{
private:
	Vec3f myPosition;
	GLfloat myHeight;				//technically, this is the height of the stem only
	GLfloat myStemRadius;
	GLboolean isOutlineEnabled;
	GLfloat myRotation;
	GLfloat myLineWidth;
	GLfloat myScaleFactor;
	struct tm* myTimeInfo;
	weather_t* myCurrentWeather;
	weather_t myPreviousWeather;	//previous weather information
	GLboolean isChangingDrooping; 
	GLboolean myLightingOn;
	GLint myJiggleCounter;
	GLboolean isJiggling;
	
	GLfloat firstQuadrant[4][4][3];
	GLfloat secondQuadrant[4][4][3];
	GLfloat thirdQuadrant[4][4][3];
	GLfloat fourthQuadrant[4][4][3];
	GLfloat x_offset, y_offset, z_offset;
	
	GLboolean up;
	GLboolean left;
	
public:
	Sunflower(weather_t* weather)
	{
		myPosition = Vec3f();
		myHeight = randFloatRange(1.5, 2.5);
		myStemRadius = 0.025f;
		isOutlineEnabled = false;
		myRotation = randFloatRange(-5.0f, 5.0f);
		myLineWidth = 0.5f;
		myScaleFactor = 1.0f;
		myTimeInfo = Utility::getTime();
		if(myTimeInfo->tm_hour < 6 || myTimeInfo->tm_hour > 18)
			myLightingOn = false;
		else 
		{
			myLightingOn = true;
		}		
		myCurrentWeather = weather;
		myPreviousWeather = *myCurrentWeather;
		if(*myCurrentWeather == CLOUDY || *myCurrentWeather == RAINY)
			isChangingDrooping = true;
		else 
		{
			isChangingDrooping = false;
		}
		myJiggleCounter = 12;
		
        up = false;
		left = false;
        // up = true;
		setControlPoints();
	}
	
	~Sunflower()
	{
		delete myTimeInfo;
	}
	
	void setPosition(GLfloat x, GLfloat y, GLfloat z)
	{
		myPosition *= 0.0f;
		myPosition += Vec3f(x, y - 0.1f, z);
	}
	
	void scale()
	{
		myScaleFactor = 4.0f;
	}
	
	void draw()
	{
//		switchToOrtho();
		glPushMatrix();
		Color4f greenOutline = Color4f(0.05f, 0.17f, 0.0f);
		
		//########################################################## Positioning
		glTranslatef(myPosition[0], myPosition[1], myPosition[2]);
		glScalef(myScaleFactor, myScaleFactor, myScaleFactor);
		glRotatef(myRotation, 0.0f, 1.0f, 0.0f);
		
		//########################################################## Stem
		glColor3f(0.14f, 0.26f, 0.09f);
		drawStem();
		
		//########################################################## Leaves
		glTranslatef(0.0f, myHeight/2.0f, 0.0f);
		glRotatef(35.0f, 0.0f, 0.0f, 1.0f);		
		drawLeaf(myHeight/3.2f);
		glRotatef(-35.0f, 0.0f, 0.0f, 1.0f);
		
		glTranslatef(0.0f, myHeight/10.0f, 0.0f);
		glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
		glRotatef(45.0f, 0.0f, 0.0f, 1.0f);
		drawLeaf(myHeight/3.5f);
		glRotatef(-45.0f, 0.0f, 0.0f, 1.0f);
		glRotatef(-180.0f, 0.0f, 1.0f, 0.0f);
		
		//########################################################## Center
		glTranslatef(firstQuadrant[3][0][0], 4.0f*myHeight/10.0f - (myHeight - firstQuadrant[3][0][1]), firstQuadrant[3][0][2]);
		glRotatef((1 - firstQuadrant[3][0][1]/myHeight)*90.0f, 1.0f, 0.0f, 0.0f);
		glPushMatrix();
		glColor3f(0.12f, 0.05f, 0.00f);		//brown
		glScalef(1.0f, 1.0f, 0.5f);
		drawSphere(myHeight/9.5f, 20, 20);
		glPopMatrix();
		
		//########################################################## Petals
		if(!myLightingOn)
			glDisable(GL_LIGHTING);
		glColor3f(0.78f, 0.68f, 0.0f);	//yellow
		drawPetals(myHeight/7.0f);
		
		glPopMatrix();
		glEnable(GL_LIGHTING);
//		switchToProj();
	}
	
	void update()
	{
//		myRotation += 5.0f;
//		jiggle();
		if(isJiggling)
		{
			jiggle();
			myJiggleCounter--;
			if(myJiggleCounter < 0)
			{
				myJiggleCounter = 12;
				isJiggling = false;
			}
		}
		if(*myCurrentWeather != myPreviousWeather)
		{
			if(*myCurrentWeather == RAINY && myPreviousWeather == CLOUDY)
				isChangingDrooping = false;
			else if(*myCurrentWeather == CLOUDY && myPreviousWeather == RAINY)
				isChangingDrooping = false;
			else {
				isChangingDrooping = true;
			}
            myPreviousWeather = *myCurrentWeather;
		}
		if(isChangingDrooping)
		{
			changeDrooping();
		}
		
		myTimeInfo = Utility::getTime();
		if(myTimeInfo->tm_hour < 6 || myTimeInfo->tm_hour > 18)
			myLightingOn = false;
		else 
		{
			myLightingOn = true;
		}

	}
	
	GLfloat getXPosition()
	{
        return myPosition[0];
	}
	
private:	
	void drawPetals(GLfloat radius)
	{
		GLfloat angle = 180.0f;
		for(int k = 0; k<12; k++)
		{
			angle += 360.0/12;
			glPushMatrix();
			glRotatef(angle, 0.0f, 0.0f, 1.0f);
			glTranslatef(0.0f, 0.9f*radius, 0.0f);
//			glRotatef(10.0f, 0.0f, 1.0f, 0.0f);
			drawPetal(radius);
			glPopMatrix();
		}
	}
	
	void drawPetal(GLfloat radius)
	{
		GLfloat resolution = 100;
		glBegin(GL_TRIANGLE_STRIP);
		glVertex3f(0.0f, -radius/2.0f, 0.0f);
		for(GLfloat k = 1; k<resolution; k++)
		{
			GLfloat y_vertex = -radius/2.0f + k*(radius/resolution);
			GLfloat x_vertex = (-1.0f/radius) * pow(y_vertex, 2) + radius/4.0f;
			glVertex3f(x_vertex, y_vertex, 0.0f);
			
			x_vertex *= -1;
			glVertex3f(x_vertex, y_vertex, 0.0f);
		}
		glVertex3f(0.0f, radius/2.0f, 0.0f);
		glEnd();
		
		if(isOutlineEnabled)
		{
			glPushMatrix();
			glTranslatef(0.0f, 0.0f, -0.0000001f);
			glScalef(1.2f, 1.2f, 1.2f);
			glColor3f(0.12f, 0.03f, 0.0f);		//yellow outline
			glBegin(GL_TRIANGLE_STRIP);
			glVertex3f(0.0f, -radius/2.0f, 0.0f);
			for(GLfloat k = 1; k<resolution; k++)
			{
				GLfloat y_vertex = -radius/2.0f + k*(radius/resolution);
				GLfloat x_vertex = (-1.0f/radius) * pow(y_vertex, 2) + radius/4.0f;
				glVertex3f(x_vertex, y_vertex, 0.0f);
				
				x_vertex *= -1;
				glVertex3f(x_vertex, y_vertex, 0.0f);
			}
			glVertex3f(0.0f, radius/2.0f, 0.0f);
			glEnd();
			glPopMatrix();
		}
	}
	
	void drawStem()
	{
		glPushMatrix();
		glEnable(GL_AUTO_NORMAL);
		glEnable(GL_MAP2_VERTEX_3);
		
		glMap2f(GL_MAP2_VERTEX_3,
				0.0, 1.0,  /* U ranges 0..1 */
				3,         /* U stride, 3 floats per coord */ 
				4,         /* U is 3rd order, i.e. cubic */
				0.0, 1.0,  /* V ranges 0..1 */
				4 * 3,     /* V stride, 4 rows, 3 floats per coord */
				4,         /* V is 3rd order, i.e. cubic */
				&firstQuadrant[0][0][0]);	/* control points */
		
		glMapGrid2f(
					10, 0.0, 1.0,
					20, 0.0, 1.0);
		
		glEvalMesh2(GL_FILL,
					0, 10,   /* Starting at 0 mesh 5 steps (rows). */
					0, 20);  /* Starting at 0 mesh 6 steps (columns). */
		
		glMap2f(GL_MAP2_VERTEX_3, 0.0, 1.0,	3, 4, 0.0, 1.0,	4*3, 4,	&secondQuadrant[0][0][0]);
		glMapGrid2f(10, 0.0, 1.0, 20, 0.0, 1.0);
		glEvalMesh2(GL_FILL, 0, 10,	0, 20);
		
		glMap2f(GL_MAP2_VERTEX_3, 0.0, 1.0,	3, 4, 0.0, 1.0,	4*3, 4,	&thirdQuadrant[0][0][0]);
		glMapGrid2f(10, 0.0, 1.0, 20, 0.0, 1.0);
		glEvalMesh2(GL_FILL, 0, 10,	0, 20);
		
		glMap2f(GL_MAP2_VERTEX_3, 0.0, 1.0,	3, 4, 0.0, 1.0,	4*3, 4,	&fourthQuadrant[0][0][0]);
		glMapGrid2f(10, 0.0, 1.0, 20, 0.0, 1.0);
		glEvalMesh2(GL_FILL, 0, 10,	0, 20);
		
		glDisable(GL_MAP2_VERTEX_3);
		glDisable(GL_AUTO_NORMAL);
		glPopMatrix();
	}
	
	void drawLeaf(GLfloat radius)
	{
		int resolution = 100;
		GLfloat z_vertex_buffer[resolution];
		
		glPushMatrix();
		glRotatef(-20.0f, 1.0f, 0.0f, 0.0f);
		glBegin(GL_TRIANGLE_STRIP);
		glVertex3f(0.0f, 0.0f, 0.0f);
		for(int k = 1; k<resolution; k++)
		{
			GLfloat x_vertex = k*(radius/resolution);
			GLfloat z_vertex;
			if(x_vertex < (3.0f/8.0f)*radius)
			{
				z_vertex = (-16.0f/(9.0f*radius)) * pow(x_vertex - (3.0f/8.0f)*radius, 2) + 2.0f*radius/8.0f;
			}
			else
			{
				z_vertex = (-16.0f/(25.0f*radius)) * pow(x_vertex - (3.0f/8.0f)*radius, 2) + 2.0f*radius/8.0f;
			}

			glVertex3f(x_vertex, 0.0f, z_vertex);
			z_vertex_buffer[k] = z_vertex;
			
			glVertex3f(x_vertex, 0.0f, 0.0f);
//			z_vertex *= -1;
//			glVertex3f(x_vertex, 0.0f, z_vertex);
		}
		glVertex3f(radius, 0.0f, 0.0f);		
		glEnd();
		glPopMatrix();
		
		glPushMatrix();
		glRotatef(35.0f, 1.0f, 0.0f, 0.0f);
		glBegin(GL_TRIANGLE_STRIP);
		glVertex3f(0.0f, 0.0f, 0.0f);
		for(int k = 1; k<resolution; k++)
		{
			GLfloat x_vertex = k*(radius/resolution);
			GLfloat z_vertex = z_vertex_buffer[k];
			z_vertex *= -1;
			glVertex3f(x_vertex, 0.0f, z_vertex);
			
			glVertex3f(x_vertex, 0.0f, 0.0f);
		}
		glVertex3f(radius, 0.0f, 0.0f);		
		glEnd();
		glPopMatrix();
	}
	
	void drawSphere(GLfloat r, int lats, int longs) 
	{
		glFrontFace(GL_CW);			// Set front-facing polygons to clockwise
		
		int i, j;
		GLfloat cellShadeVal, dotProduct;
		Vec3f lightVector(-20.0f, 50.0f, 45.0f);
		lightVector = lightVector.normalize();
		Vec3f temp(-11, 30, lightVector[2]);
		temp = temp.normalize();
		Vec3f temp1(10, 45, lightVector[2]);
		temp1 = temp1.normalize();
		
		for(i = 0; i <= lats; i++) {
			GLfloat lat0 = M_PI * (-0.5 + (GLfloat) (i - 1) / lats);
			GLfloat z0  = sin(lat0);
			GLfloat zr0 =  cos(lat0);
			
			GLfloat lat1 = M_PI * (-0.5 + (GLfloat) i / lats);
			GLfloat z1 = sin(lat1);
			GLfloat zr1 = cos(lat1);
			
			glBegin(GL_QUAD_STRIP);
			for(j = 0; j <= longs; j++) {
				GLfloat lng = 2 * M_PI * (GLfloat) (j - 1) / longs;
				GLfloat x = cos(lng);
				GLfloat y = sin(lng);
				
				Vec3f normal(x*zr0, y*zr0, z0);
				//				glNormal3f(x * zr0, y * zr0, z0);
				dotProduct = (normal.normalize()).dot(lightVector);
				cellShadeVal = getGradientVal(dotProduct);
				glColor3f(0.12f+cellShadeVal, 0.05f+cellShadeVal, 0.0f+cellShadeVal);
				glVertex3f(r * x * zr0, r * y * zr0, r * z0);
				
				normal = Vec3f(x*zr1, y*zr1, z1);
				//				glNormal3f(x * zr1, y * zr1, z1);
				dotProduct = (normal.normalize()).dot(lightVector);
				cellShadeVal = getGradientVal(dotProduct);
				glColor3f(0.12f+cellShadeVal, 0.05f+cellShadeVal, 0.0f+cellShadeVal);
				glVertex3f(r * x * zr1, r * y * zr1, r * z1);
			}
			glEnd();
		}
		if(isOutlineEnabled)
		{
			Color4f brownOutline = Color4f(0.12f, 0.05f, 0.0f);
			Utility::enableOutlining(brownOutline, myLineWidth);
			for(i = 0; i <= lats; i++) {
				GLfloat lat0 = M_PI * (-0.5 + (GLfloat) (i - 1) / lats);
				GLfloat z0  = sin(lat0);
				GLfloat zr0 =  cos(lat0);
				
				GLfloat lat1 = M_PI * (-0.5 + (GLfloat) i / lats);
				GLfloat z1 = sin(lat1);
				GLfloat zr1 = cos(lat1);
				
				glBegin(GL_QUAD_STRIP);
				for(j = 0; j <= longs; j++) {
					GLfloat lng = 2 * M_PI * (GLfloat) (j - 1) / longs;
					GLfloat x = cos(lng);
					GLfloat y = sin(lng);
					glVertex3f(r * x * zr0, r * y * zr0, r * z0);
					glVertex3f(r * x * zr1, r * y * zr1, r * z1);
				}
				glEnd();
			}
			Utility::disableOutlining();		
		}
		glFrontFace(GL_CCW);
	}
	
	GLfloat getGradientVal(GLfloat dotProduct)
	{		
		if(dotProduct < 0.15)
			return 0.01;
		else
			return 0.08;
	}
	
	void setControlPoints()
	{
		for(int i = 0; i<4; i++)
		{
			if(i == 0)
			{
				x_offset = 0.0f;
				y_offset = 0.0f;
				z_offset = 0.0f;
			}
			else if(i == 1)
			{
				x_offset = 0.0f;
				y_offset = 2.0f*myHeight/3.0f;
				z_offset = 0.0f;
			}
			else if(i == 2)
			{
				x_offset = 0.0f;
				y_offset = 3.0f*myHeight/4.0f;
				z_offset = 0.0f;
			}
			else 
			{
				x_offset = 0.0f;
				y_offset = myHeight;
				z_offset = myHeight/8.0f;
			}
			
			//############## [i][0]
			firstQuadrant[i][0][0] = 0.0f + x_offset;
			firstQuadrant[i][0][1] = y_offset;
			firstQuadrant[i][0][2] = myStemRadius + z_offset;
			
			secondQuadrant[i][0][0] = -myStemRadius + x_offset;
			secondQuadrant[i][0][1] = y_offset;
			secondQuadrant[i][0][2] = 0.0f + z_offset;
			
			thirdQuadrant[i][0][0] = 0.0f + x_offset;
			thirdQuadrant[i][0][1] = y_offset;
			thirdQuadrant[i][0][2] = -myStemRadius + z_offset;
			
			fourthQuadrant[i][0][0] = myStemRadius + x_offset;
			fourthQuadrant[i][0][1] = y_offset;
			fourthQuadrant[i][0][2] = 0.0f + z_offset;
			
			//############## [i][1]
			firstQuadrant[i][1][0] = myStemRadius*MAGIC_NUMBER + x_offset;
			firstQuadrant[i][1][1] = y_offset;
			firstQuadrant[i][1][2] = myStemRadius + z_offset;
			
			secondQuadrant[i][1][0] = -myStemRadius + x_offset;
			secondQuadrant[i][1][1] = y_offset;
			secondQuadrant[i][1][2] = myStemRadius*MAGIC_NUMBER + z_offset;
			
			thirdQuadrant[i][1][0] = -myStemRadius*MAGIC_NUMBER + x_offset;
			thirdQuadrant[i][1][1] = y_offset;
			thirdQuadrant[i][1][2] = -myStemRadius + z_offset;
			
			fourthQuadrant[i][1][0] = myStemRadius + x_offset;
			fourthQuadrant[i][1][1] = y_offset;
			fourthQuadrant[i][1][2] = -myStemRadius*MAGIC_NUMBER + z_offset;
			
			//############## [i][2]
			firstQuadrant[i][2][0] = myStemRadius + x_offset;
			firstQuadrant[i][2][1] = y_offset;
			firstQuadrant[i][2][2] = myStemRadius*MAGIC_NUMBER + z_offset;
			
			secondQuadrant[i][2][0] = -myStemRadius*MAGIC_NUMBER + x_offset;
			secondQuadrant[i][2][1] = y_offset;
			secondQuadrant[i][2][2] = myStemRadius + z_offset;
			
			thirdQuadrant[i][2][0] = -myStemRadius + x_offset;
			thirdQuadrant[i][2][1] = y_offset;
			thirdQuadrant[i][2][2] = -myStemRadius*MAGIC_NUMBER + z_offset;
			
			fourthQuadrant[i][2][0] = myStemRadius*MAGIC_NUMBER + x_offset;
			fourthQuadrant[i][2][1] = y_offset;
			fourthQuadrant[i][2][2] = -myStemRadius + z_offset;
			
			//############## [i][3]
			firstQuadrant[i][3][0] = myStemRadius + x_offset;
			firstQuadrant[i][3][1] = y_offset;
			firstQuadrant[i][3][2] = 0.0f + z_offset;
			
			secondQuadrant[i][3][0] = 0.0f + x_offset;
			secondQuadrant[i][3][1] = y_offset;
			secondQuadrant[i][3][2] = myStemRadius + z_offset;
			
			thirdQuadrant[i][3][0] = -myStemRadius + x_offset;
			thirdQuadrant[i][3][1] = y_offset;
			thirdQuadrant[i][3][2] = 0.0f + z_offset;
			
			fourthQuadrant[i][3][0] = 0.0f + x_offset;
			fourthQuadrant[i][3][1] = y_offset;
			fourthQuadrant[i][3][2] = -myStemRadius + z_offset;
		}
	}
	
	void changeDrooping()
	{
		if(up)
		{
			GLfloat riseSpeed = myHeight/10.0f;
			for(int k = 0; k<4; k++)
			{
				firstQuadrant[3][k][1] += riseSpeed;
				secondQuadrant[3][k][1] += riseSpeed;
				thirdQuadrant[3][k][1] += riseSpeed;
				fourthQuadrant[3][k][1] += riseSpeed;
			}
		}
		else 
		{
			GLfloat droopSpeed = myHeight/100.0f;
			for(int k = 0; k<4; k++)
			{
				firstQuadrant[3][k][1] -= droopSpeed;
				secondQuadrant[3][k][1] -= droopSpeed;
				thirdQuadrant[3][k][1] -= droopSpeed;
				fourthQuadrant[3][k][1] -= droopSpeed;
			}
		}
		
		if(up && firstQuadrant[3][0][1] > myHeight)
		{
			up = false;
			isChangingDrooping = false;
		}
		else if(!up && firstQuadrant[3][0][1] < myHeight/2.0f)
		{
			up = true;
			isChangingDrooping = false;
		}
	}
	
public:
	void jiggle()
	{
		GLfloat jiggleSpeed = myHeight/24.0f;
		if(left)
		{
			for(int k = 0; k<4; k++)
			{
				firstQuadrant[3][k][0] += jiggleSpeed;
				secondQuadrant[3][k][0] += jiggleSpeed;
				thirdQuadrant[3][k][0] += jiggleSpeed;
				fourthQuadrant[3][k][0] += jiggleSpeed;
			}
		}
		else 
		{
			for(int k = 0; k<4; k++)
			{
				firstQuadrant[3][k][0] -= jiggleSpeed;
				secondQuadrant[3][k][0] -= jiggleSpeed;
				thirdQuadrant[3][k][0] -= jiggleSpeed;
				fourthQuadrant[3][k][0] -= jiggleSpeed;
			}		
		}
		if(left && firstQuadrant[3][0][0] > myHeight/8.0f)
		{
			left = false;
		}
		else if(!left && firstQuadrant[3][0][0] < -myHeight/8.0f)
		{
			left = true;
		}
	}
	
	void setJiggling()
	{
		isJiggling = true;
	}
};

#endif /* end of include guard: SUNFLOWER_H_P3WE672Q */
